﻿using Asion.Dapper.SqlBuilder;
using Asion.Dapper.SqlBuilder.Condition;
using Dapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.Linq;
using System.Text;


namespace Asion.Dapper
{
    public interface IWhere<TEntity> : ICondition where TEntity : IEntity
    {

    }

    public class EqualsWhere<TEntity> : BaseCondition<TEntity>, IWhere<TEntity>, IConditionDynamicParameters, IGetSql where TEntity : IEntity
    {
        private IDictionary<string, string> where;

        public EqualsWhere(IDictionary<string, string> dic)
        {
            where = dic;
        }

        public string GetSql()
        {
            var result = string.Empty;
            if (where == null || !where.Any())
            {
                throw new Exception("sql must have where condition");
            }
            StringBuilder sql = new StringBuilder();
            int i = 0;
            foreach (var column in this.where)
            {
                if (i > 0)
                    sql.Append(" AND ");
                var mapping = _custom_table_mappinghandle.GetCustomTableMapping().GetColumnMapping(column.Key);
                if (mapping == null)
                {
                    throw new ArgumentException($" column that {column.Key} does not exist in where dic");
                }

                var formula = ComplexOperatorFactory.GetComplexCondtionOperatorFormula(CondtionOperator.Equals);
                sql.AppendFormat(formula, _encap_sulation.EncapSulation(mapping.DataColumnName), _encap_sulation.EncapCloumnParam(mapping.DataColumnName));
                AddDynamicParam(mapping.DataColumnName, column.Value, mapping.GetDataColumnType(), CondtionOperator.Equals);
                // not param clean
                i++;
            }
            if (i > 0)
            {
                result = $" WHERE {sql.ToString()}";
            }
            return result;

        }



        public DynamicParameters GetDynamicParameters()
        {
            return new DynamicParameters(where);
        }


    }







    public class ComplexWhere<TEntity> : BaseCondition<TEntity>, IWhere<TEntity>, IConditionDynamicParameters, IGetSql, IPrintSql where TEntity : IEntity
    {
        internal List<ComplexCondtion> Conditions;

        internal bool IsChild { get; set; }

        public ComplexWhere()
        {
            Conditions = new List<ComplexCondtion>();
        }



        public ComplexWhere(bool isChild) : base()
        {
            IsChild = true;
        }

        public ComplexWhere(List<ComplexCondtion> conditions) : base()
        {
            Conditions = conditions;
        }

        internal void SetChild(bool isChild)
        {
            IsChild = isChild;
        }


        public ComplexWhere<TEntity> Eq(string column, object value, string columnNick = "")
        {

            Conditions.Add(new ComplexCondtion(column, CondtionOperator.Equals, value, columnNick));
            return this;
        }

        public ComplexWhere<TEntity> NotEq(string column, object value, string columnNick = "")
        {

            Conditions.Add(new ComplexCondtion(column, CondtionOperator.NotEquals, value, columnNick));
            return this;
        }


        /// <summary>
        /// Greater than
        /// </summary>
        /// <param name="column"></param>
        /// <param name="value"></param>
        /// <param name="columnNick"></param>
        /// <returns></returns>
        public ComplexWhere<TEntity> Gt(string column, object value, bool isEquals = false, string columnNick = "")
        {
            var oprator = isEquals ? CondtionOperator.GreaterAndEquals : CondtionOperator.Greater;
            Conditions.Add(new ComplexCondtion(column, oprator, value, columnNick));
            return this;
        }

        public ComplexWhere<TEntity> Like(string column, object value, string columnNick = "")
        {

            Conditions.Add(new ComplexCondtion(column, CondtionOperator.Like, value, columnNick));
            return this;
        }

        public ComplexWhere<TEntity> In(string column, string[] values, string columnNick = "")
        {
            var value = string.Join(",", values);
            Conditions.Add(new ComplexCondtion(column, CondtionOperator.In, value, columnNick));
            return this;
        }

        /// <summary>
        /// 
        /// <para>DATE(order_date) = '2021-03-15';</para>
        /// </summary>
        /// <param name="column"></param>
        /// <param name="value"></param>
        /// <param name="columnNick"></param>
        /// <returns></returns>
        public ComplexWhere<TEntity> EqDate(string column, object value, string columnNick = "")
        {

            Conditions.Add(new ComplexCondtion(column, CondtionOperator.EqualsDate, value, columnNick));
            return this;
        }

        private bool GetIsParenthesis()
        {
            int count = Conditions.Count();
            if (IsChild && count != 1)
            {
                return true;
            }
            return false;
        }



        public string GetSql()
        {
            return BuildSql();
        }

        private string BuildSql(bool isPrint = false)
        {
            var result = string.Empty;
            if (Conditions == null || !Conditions.Any())
            {
                throw new Exception("sql must have where condition");
            }

            bool hasParenthesis = GetIsParenthesis();

            StringBuilder sb = new StringBuilder();
            int i = 0;
            foreach (var column in this.Conditions)
            {

                if (i > 0)
                    sb.Append(" AND ");
                var mapping = _custom_table_mappinghandle.GetCustomTableMapping().GetColumnMapping(column.Column);
                if (mapping == null)
                {
                    throw new ArgumentException($" column that {column.Column} does not exist in where dic");
                }

                var formula = ComplexOperatorFactory.GetComplexCondtionOperatorFormula(column.Operator);
                sb.AppendFormat(formula, _encap_sulation.EncapSulation(mapping.DataColumnName), _encap_sulation.EncapCloumnParam(column.Guid));
                AddDynamicParam(column.Guid, column.Value, mapping.GetDataColumnType(), column.Operator);
                i++;


            }
            if (i > 0 && !IsChild)
            {
                result = $" WHERE {sb.ToString()}";
            }
            else
            {
                if (GetIsParenthesis())
                {
                    result = $"({sb.ToString()})";
                }
                else
                {
                    result = sb.ToString();
                }
            }
            return result;
        }

        public DynamicParameters GetDynamicParameters()
        {
            return DynamicParam;
        }

        public string PrintSql()
        {
            var whereSql = BuildSql(true);
            Console.WriteLine($"# where sql:{whereSql} # dynamic param : {DynamicParametersUtils.GetNames(DynamicParam)}");
            return whereSql;
        }
    }


    public class ComplexCondtion : BaseParam
    {
        public ComplexCondtion(string column, CondtionOperator operators, object value)
        {
            this.Column = column;
            this.Operator = operators;
            this.Value = value;
            //指定nick
            this.Guid = CreateGuid(column, operators, string.Empty);
        }

        public ComplexCondtion(string column, CondtionOperator operators, object value, string valueNick)
        {
            this.Column = column;
            this.Operator = operators;
            this.Value = value;
            //指定nick
            this.Guid = CreateGuid(column, operators, valueNick);
        }
        public string Column { get; set; }


        public CondtionOperator Operator { get; set; }


        public object Value { get; set; }

        /// <summary>
        /// guid 生成规则
        /// </summary>
        /// <returns></returns>
        public string CreateGuid(string column, CondtionOperator operators, string valueNick)
        {
            //如果是指定的nickName则直接指定即可
            if (!string.IsNullOrWhiteSpace(valueNick))
            {
                return valueNick;
            }
            //能解决部分值是一样的问题
            var guid = column.ToLower() + "_" + operators.ToString().ToLower();
            return guid;
        }
    }

    public enum CondtionOperator
    {
        Greater,
        Less,
        GreaterAndEquals,
        LessAndEquals,
        /// <summary>
        /// DATE(order_date) = '2021-03-15';
        /// </summary>
        EqualsDate,
        Equals,
        NotEquals,
        LeftLike,
        RightLike,
        Like,
        In,
        NotIn,
        Set,
        BatchSet,
        IncreaseSet,
        ReduceSet,

    }

    public class ComplexOperatorFactory
    {
        public static string GetComplexCondtionOperatorFormula(CondtionOperator type)
        {
            var result = string.Empty;
            switch (type)
            {
                case CondtionOperator.Equals:
                    result = " {0} = {1} ";
                    break;
                case CondtionOperator.NotEquals:
                    result = " {0} != {1} ";
                    break;
                case CondtionOperator.Greater:
                    result = " {0} > {1} ";
                    break;
                case CondtionOperator.GreaterAndEquals:
                    result = " {0} >= {1} ";
                    break;
                case CondtionOperator.Less:
                    result = " {0} < {1} ";
                    break;
                case CondtionOperator.LessAndEquals:
                    result = " {0} <= {1} ";
                    break;
                case CondtionOperator.LeftLike:
                    result = " {0} LIKE  CONCAT('%',{1}) ";
                    break;
                case CondtionOperator.RightLike:
                    result = "{0} LIKE CONCAT({1},'%') ";
                    break;
                case CondtionOperator.Like:
                    result = " {0} LIKE CONCAT('%',{1},'%') ";
                    break;
                case CondtionOperator.In:
                    result = " {0} IN{1}";
                    break;
                case CondtionOperator.NotIn:
                    result = " {0} NOT IN{1}";
                    break;
                case CondtionOperator.Set:
                    result = " {0} = {1} ";
                    break;
                case CondtionOperator.BatchSet:
                    result = " {0} = ";
                    break;
                case CondtionOperator.IncreaseSet:
                    result = " {0} = {0}+{1} ";
                    break;
                case CondtionOperator.ReduceSet:
                    result = " {0} = {0}-{1} ";
                    break;
                case CondtionOperator.EqualsDate:
                    result = "  DATE({0}) = {1}";
                    break;
                default:
                    throw new NotImplementedException();
                    break;
            }
            return result;
        }
    }

    public static class ComplexWhereExtend
    {
        public static ComplexWhere<TEntity> Eq<TEntity>(this ComplexWhere<TEntity> condtion, string column, string value) where TEntity : IEntity
        {
            return condtion.Eq(column, value);
        }

        public static ComplexWhere<TEntity> Like<TEntity>(this ComplexWhere<TEntity> condtion, string column, string value) where TEntity : IEntity
        {
            return condtion.Like(column, value);
        }

        public static ComplexWhere<TEntity> In<TEntity>(this ComplexWhere<TEntity> condtion, string column, string[] values) where TEntity : IEntity
        {
            return condtion.In(column, values);
        }
    }

    public class MultComplexWhere<TEntity> : BaseCondition<TEntity>, IWhere<TEntity>, IConditionDynamicParameters, IPrintSql, IGetSql where TEntity : IEntity
    {


        internal List<MultComplex<TEntity>> conditions;

        public MultComplexWhere()
        {
            conditions = new List<MultComplex<TEntity>>();
        }


        public void And(Func<ComplexWhere<TEntity>> func)
        {
            conditions.Add(new MultComplex<TEntity>(MultComplexOperator.And, func.Invoke()));
        }





        public void Or(Func<ComplexWhere<TEntity>> func)
        {
            conditions.Add(new MultComplex<TEntity>(MultComplexOperator.Or, func.Invoke()));
        }





        public DynamicParameters GetDynamicParameters()
        {
            return DynamicParam;
        }

        public string GetSql()
        {

            return GetSql();

        }

        private string BuildSql(bool isPrint = false)
        {
            var result = string.Empty;
            if (conditions == null || !conditions.Any())
            {
                throw new Exception("sql must have where condition");
            }
            StringBuilder sb = new StringBuilder();
            int i = 0;
            foreach (var column in this.conditions)
            {
                if (i != 0)
                {
                    switch (column.Operator)
                    {
                        case MultComplexOperator.And:
                            sb.Append(" AND ");
                            break;
                        case MultComplexOperator.Or:
                            sb.Append(" OR ");
                            break;
                        default:
                            throw new NotImplementedException();
                            break;
                    }
                }
                var conditon = column.Value;
                conditon.SetChild(true);
                var sql = conditon.GetSql();
                if (!isPrint)
                {
                    AddDynamicParam(conditon.GetDynamicParameters());
                }
                sb.Append(sql);
                i++;
            }
            if (i > 0)
            {
                result = $" WHERE {sb.ToString()}";
            }
            return result;
        }


        public string PrintSql()
        {
            return BuildSql(true);
        }
    }


    internal class MultComplex<TEntity> where TEntity : IEntity
    {
        internal MultComplex(MultComplexOperator operators, ComplexWhere<TEntity> value)
        {
            this.Operator = operators;
            this.Value = value;
        }




        internal MultComplexOperator Operator { get; set; }


        internal ComplexWhere<TEntity> Value { get; set; }
    }

    internal enum MultComplexOperator
    {
        And,
        Or
    }


    public class WhereFactory<TEntity> : BaseCondition<TEntity> where TEntity : IEntity
    {
        public WhereFactory(IWhere<TEntity> condtion, IEncapSulation encapSulation)
        {
            where = condtion;
            _encap_sulation = encapSulation;

        }

        private IWhere<TEntity> where;


        internal string GetSql()
        {

            if (where is EqualsWhere<TEntity>)
            {
                var condtion = where as EqualsWhere<TEntity>;
                var result = condtion.GetSql();
                return result;
            }

            if (where is ComplexWhere<TEntity>)
            {
                var condtion = where as ComplexWhere<TEntity>;
                var result = condtion.GetSql();
                return result;
            }

            if (where is MultComplexWhere<TEntity>)
            {
                var condtion = where as MultComplexWhere<TEntity>;
                var result = condtion.GetSql();
                return result;
            }
            throw new NotImplementedException("where condition");
        }
    }

}